package org.wsff.tools.api.gateway.manager;

import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.wsff.tools.api.gateway.core.Proxy;
import org.wsff.tools.api.gateway.core.ProxyFilter;
import org.wsff.tools.api.gateway.core.ProxyInvoker;
import org.wsff.tools.api.gateway.core.ProxyValidator;
import org.wsff.tools.api.gateway.error.GatewayException;

import com.google.common.collect.Maps;

/**
 * 网关服务提供者抽象类
 *
 * @author ryan
 * @version Id: AbstractProxyManager.java, v 0.1 2022-01-19 14:16 ryan Exp $$
 */
@Slf4j
public abstract class AbstractProxyManager<Request extends Serializable, Response extends Serializable> implements ProxyManager<Request, Response>, ProviderChangeListener {

    /** 服务代理集合 */
    protected Map<String, ProxyWrapper<Request, Response>> PROXY_KEEPER = Maps.newConcurrentMap();

    /** lock */
    private final ReadWriteLock                            lock         = new ReentrantReadWriteLock();

    /**
     * 获取代理服务实例
     *
     * @param name 服务名称
     * @return 服务实例
     */
    @Override
    public ProxyWrapper<Request, Response> getInvoker(String name) {
        return PROXY_KEEPER.get(name);
    }

    /**
     * 获取代理服务过滤器实例
     *
     * @param name proxy name
     * @param type filter type
     * @return 过滤器实例
     */
    @Override
    public ProxyFilter<Request, Response> getFilterByType(String name, String type) {
        ProxyWrapper<Request, Response> wrapper = PROXY_KEEPER.get(name);
        if (wrapper.isOffline()) {
            return null;
        }
        Map<String, ProxyFilter<Request, Response>> filters = wrapper.getProxyFilters();
        if (filters == null || filters.isEmpty()) {
            return null;
        }
        return filters.get(type);
    }

    /**
     * 获取代理服务校验器实例
     *
     * @param name proxy name
     * @return 校验器实例
     */
    @Override
    public ProxyValidator<Request> getValidator(String name) {
        ProxyWrapper<Request, Response> wrapper = PROXY_KEEPER.get(name);
        if (wrapper.isOffline()) {
            return null;
        }
        return wrapper.getProxyValidator();
    }

    /**
     * 开始
     *
     * @throws GatewayException 异常
     */
    @Override
    public void start() throws GatewayException {
        registerAllProxy();
    }

    /**
     * 注册服务
     */
    private void registerAllProxy() {
        // 注册服务代理
        registerProxyInvokers();

        // 注册服务代理校验器
        registerProxyValidators();

        // 注册服务代理过滤器
        registerProxyFilters();
    }

    /**
     * 注册服务代理过滤器
     */
    protected void registerProxyFilters() {
        buildProxyFilters(PROXY_KEEPER);
    }

    /**
     * 获取所有 ProxyFilter
     * 
     * @return filter集合
     */
    protected abstract List<ProxyFilter<Request, Response>> getAllProxyFilters();

    /**
     * 注册服务代理校验器
     */
    protected void registerProxyValidators() {
        buildProxyValidators(PROXY_KEEPER);
    }

    /**
     * 获取所有ProxyValidator
     * 
     * @return 验证器集合
     */
    protected abstract List<ProxyValidator<Request>> getAllProxyValidators();

    /**
     * 注册服务代理
     */
    protected void registerProxyInvokers() {
        buildProxyInvokers(PROXY_KEEPER);
    }

    /**
     * 获取所有ProxyInvoker
     * 
     * @return 服务集合
     */
    protected abstract List<ProxyInvoker<Request, Response>> getAllProxyInvokers();

    /**
     * 获取服务原始对象
     *  服务对象可能会被代理，需要获取被代理的原始对象
     * 
     * @param obj 代理对象
     * @return 原始对象
     */
    protected Object getOriginalObject(Object obj) {
        return obj;
    }

    /**
     * 销毁
     *
     * @throws GatewayException 异常
     */
    @Override
    public void destroy() throws GatewayException {
        PROXY_KEEPER.clear();
    }

    /**
     * proxy 变更
     *
     * @param event event
     */
    @Override
    public void change(ProviderChangedEvent event) {
        try {
            lock.writeLock().lock();
            ProviderChangedEvent.ChangeType type = ProviderChangedEvent.ChangeType.getByCode(event.getType());
            if (type == null) {
                return;
            }
            switch (type) {
                case RELOAD:
                    reloadAllProxy();
                    break;
                case OFFLINE:
                    offlineProxyByName(event.getName());
                    break;
            }

        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 重新加载服务
     */
    protected void reloadAllProxy() {
        Map<String, ProxyWrapper<Request, Response>> tempProxy = Maps.newConcurrentMap();

        buildProxyInvokers(tempProxy);

        buildProxyFilters(tempProxy);

        buildProxyValidators(tempProxy);

        Map<String, ProxyWrapper<Request, Response>> bakProxy = PROXY_KEEPER;
        PROXY_KEEPER = tempProxy;
        bakProxy.clear();

    }

    /**
     * build Proxy Validators
     * 
     * @param tempProxy tempProxy
     */
    private void buildProxyValidators(Map<String, ProxyWrapper<Request, Response>> tempProxy) {
        List<ProxyValidator<Request>> validators = getAllProxyValidators();

        if (validators == null || validators.isEmpty()) {
            log.info("No validator cannot be Registered, there is no validator is defined.");
            return;
        }

        for (ProxyValidator<Request> validator : validators) {
            ProxyValidator<Request> proxy = (ProxyValidator<Request>) getOriginalObject(validator);
            Proxy anno = proxy.getClass().getDeclaredAnnotation(Proxy.class);
            if (anno == null) {
                throw new IllegalStateException("Cannot register ProxyValidator, please make sure place annotation @Proxy on validator: " + validator.getClass().getName());

            }
            String name = anno.id();
            ProxyWrapper<Request, Response> v = tempProxy.get(name);
            if (v == null || v.getProxyValidator() != null) {
                throw new IllegalStateException("Cannot register ProxyValidator, the service name has been register by the other validator: " + validator.getClass().getName());
            }
            v.addProxyValidator(validator);
            log.info("Register ProxyValidator: {} - {}", name, validator.getClass().getName());
        }
    }

    /**
     * build Proxy Filters
     * 
     * @param tempProxy tempProxy
     */
    private void buildProxyFilters(Map<String, ProxyWrapper<Request, Response>> tempProxy) {
        List<ProxyFilter<Request, Response>> filters = getAllProxyFilters();

        if (filters == null || filters.isEmpty()) {
            log.warn("Cannot register ProxyFilter, please make sure the filter setup as spring beans.");
            return;
        }

        // Group filters by type
        for (ProxyFilter<Request, Response> filter : filters) {
            ProxyFilter<Request, Response> proxy = (ProxyFilter<Request, Response>) getOriginalObject(filter);
            Proxy anno = proxy.getClass().getDeclaredAnnotation(Proxy.class);
            if (anno == null) {
                throw new IllegalStateException("Cannot register ProxyFilter, please make sure place annotation @Proxy on validator: " + filter.getClass().getName());
            }
            String name = anno.id();
            tempProxy.get(name).addProxyFilters(filter.filterType(), filter);
            log.info("Register ProxyFilter: {}", filter.getClass().getName());
        }
    }

    /**
     * build Proxy Invokers
     * 
     * @param tempProxy tempProxy
     */
    private void buildProxyInvokers(Map<String, ProxyWrapper<Request, Response>> tempProxy) {
        List<ProxyInvoker<Request, Response>> invokers = getAllProxyInvokers();
        if (invokers == null || invokers.isEmpty()) {
            throw new IllegalStateException("Cannot register ProxyInvoker, please make sure the ProxyInvoker setup as spring bean.");
        }

        for (ProxyInvoker<Request, Response> invoker : invokers) {
            ProxyInvoker<Request, Response> proxy = (ProxyInvoker<Request, Response>) getOriginalObject(invoker);
            Proxy anno = proxy.getClass().getDeclaredAnnotation(Proxy.class);
            if (anno == null) {
                throw new IllegalStateException("Cannot register proxy, please make sure place annotation @Proxy on ProxyInvoker: " + invoker.getClass().getName());

            }
            String name = anno.id();
            ProxyWrapper<Request, Response> registeredInvoker = tempProxy.get(name);
            if (registeredInvoker != null) {
                throw new IllegalStateException("Cannot register proxy, the service name has been register by other ProxyInvoker: " + invoker.getClass().getName());
            }
            tempProxy.put(name, ProxyWrapper.create(invoker, name));
            log.info("Register ProxyInvoker: {} - {}", name, invoker.getClass().getName());
        }
    }

    /**
     * 下线某个服务
     * 
     * @param id 服务ID
     */
    protected void offlineProxyByName(String id) {
        ProxyWrapper<Request, Response> wrapper = PROXY_KEEPER.get(id);
        if (wrapper != null) {
            wrapper.offline();
        }
    }
}
